#pragma once

#include <string>
#include <stdint.h>
#include <memory>
#include <list>
#include <fstream>
#include <sstream>
#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <stdarg.h>
#include <functional>
#include <thread>
#include "spinlock.h"
#include "singleton.h"

/*使用流的方式，将不同日志级别的事件写入logger中*/ 
#define SYLAR_LOG_LEVEL(logger, level) \
    if(logger->getLevel() <= level) \
        sylar::LogEventWrap(my_farme::LogEvent::ptr(new my_farme::LogEvent(logger, level, \
                        __FILE__, __LINE__, 0, my_farme::GetThreadId(),\
                my_farme::GetFiberId(), time(0), my_farme::Thread::GetName()))).getSS()               
#define SYLAR_LOG_DEBUG(logger) SYLAR_LOG_LEVEL(logger, my_farme::LogLevel::DEBUG)
#define SYLAR_LOG_INFO(logger) SYLAR_LOG_LEVEL(logger, my_farme::LogLevel::INFO)
#define SYLAR_LOG_WARN(logger) SYLAR_LOG_LEVEL(logger, my_farme::LogLevel::WARN)
#define SYLAR_LOG_ERROR(logger) SYLAR_LOG_LEVEL(logger, my_farme::LogLevel::ERROR)
#define SYLAR_LOG_FATAL(logger) SYLAR_LOG_LEVEL(logger, my_farme::LogLevel::FATAL)

/*使用格式化方式， 将不同日志级别的事件写入logger中*/
#define SYLAR_LOG_FMT_LEVEL(logger, level, fmt, ...) \
    if(logger->getLevel() <= level) \
        my_farme::LogEventWrap(my_farme::LogEvent::ptr(new my_farme::LogEvent(logger, level, \
                        __FILE__, __LINE__, 0, my_farme::GetThreadId(),\
                my_farme::GetFiberId(), time(0), my_farme::Thread::GetName()))).getEvent()->format(fmt, __VA_ARGS__)
#define SYLAR_LOG_FMT_DEBUG(logger, fmt, ...) SYLAR_LOG_FMT_LEVEL(logger, my_farme::LogLevel::DEBUG, fmt, __VA_ARGS__)
#define SYLAR_LOG_FMT_INFO(logger, fmt, ...)  SYLAR_LOG_FMT_LEVEL(logger, my_farme::LogLevel::INFO, fmt, __VA_ARGS__)
#define SYLAR_LOG_FMT_WARN(logger, fmt, ...)  SYLAR_LOG_FMT_LEVEL(logger, my_farme::LogLevel::WARN, fmt, __VA_ARGS__)
#define SYLAR_LOG_FMT_ERROR(logger, fmt, ...) SYLAR_LOG_FMT_LEVEL(logger, my_farme::LogLevel::ERROR, fmt, __VA_ARGS__)
#define SYLAR_LOG_FMT_FATAL(logger, fmt, ...) SYLAR_LOG_FMT_LEVEL(logger, my_farme::LogLevel::FATAL, fmt, __VA_ARGS__)
#define SYLAR_LOG_ROOT() my_farme::LoggerMgr::GetInstance()->getRoot()
#define SYLAR_LOG_NAME(name) my_farme::LoggerMgr::GetInstance()->getLogger(name)

/*获取主日志器*/
#define SYLAR_LOG_ROOT() my_farme::LoggerMgr::GetInstance()->getRoot()

/*获取相应名字的日志器*/
#define SYLAR_LOG_NAME(name) my_farme::LoggerMgr::GetInstance()->getLogger(name)

namespace my_farme {

class Logger;
class LoggerManager;

/*定义日志级别，并提供将日志级别与文本之间的互相转化*/
class LogLevel {
public:
    enum Level {
        UNKNOW = 0,
        DEBUG = 1, 
        INFO = 2,
        WARN = 3, 
        ERROR = 4,
        FATAL = 5
    };
    /*将日志级别转化为文本*/
    static const char* ToString(LogLevel::Level level);
    /*将文本转化为日志级别*/
    static LogLevel::Level FromString(const std::string& str);
};

/*记录日志事件，记录相关日志信息*/
class LogEvent {
public:
    typedef std::shared_ptr<LogEvent> ptr;
    /*构造函数，需要注意初始化列表的初始化顺序*/
    LogEvent(std::shared_ptr<Logger> logger, LogLevel::Level level,
            const char* file, int32_t line, uint32_t elapse,
            uint32_t thread_id, uint32_t fiber_id, uint64_t time,
            const std::string& thread_name);

    /*获取成员变量的值， 一般不改变变量的值，就要写成const函数*/
    const char* getFile() const { return m_file;}
    int32_t getLine() const { return m_line;}
    uint32_t getElapse() const { return m_elapse;}
    uint32_t getThreadId() const { return m_threadId;}
    uint32_t getFiberId() const { return m_fiberId;}
    uint64_t getTime() const { return m_time;}
    std::string getContent() const { return m_ss.str();}
    const std::string getThreadName() const { return m_threadName;}
    std::stringstream& getSS() { return m_ss;}
    std::shared_ptr<Logger> getLogger() const { return m_logger;}
    LogLevel::Level getLevel() const { return m_level;}

    /*格式化写入日志内容*/
    void format(const char* fmt, ...);
    void format(const char* fmt, va_list al);

private:
    const char* m_file = nullptr;       //文件名
    int32_t m_line = 0;                 //行号
    uint32_t m_elapse = 0;              //程序启动到现在的毫秒数
    uint32_t m_threadId = 0;            //线程id
    uint32_t m_fiberId = 0;             //协程id
    uint64_t m_time = 0;                //时间戳
    std::string m_threadName;           //线程名，为什么需要线程名称？
    std::stringstream m_ss;             //日志内容流
    std::shared_ptr<Logger> m_logger;   //日志器，声明一个指针，指向Logger对象或其派生类对象
    LogLevel::Level m_level;            //日志级别
};

/*日志事件包装器，将LogEvent打包。可通过该类完成对日志的定义*/
class LogEventWrap {
public:
    /*构造函数，初始化要包装的LogEvent*/
    LogEventWrap(LogEvent::ptr e);
    /*析构函数，在析构的时候将LogEvent写到Logger里面*/
    ~LogEventWrap();
    LogEvent::ptr getEvent() const { return m_event;}
    std::stringstream& getSS();
private:
    LogEvent::ptr m_event;       //日志事件
};

/*日志格式初始化*/
class LogFormatter {
public:
    typedef std::shared_ptr<LogFormatter> ptr;
    /*构造函数，初始化日志格式，并解析*/
    LogFormatter(const std::string& pattern);
    /*将解析后的日志信息输出到流中*/
    std::string format(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event);
    class FormatItem {//共有类内成员，通过该类得到解析后的格式，是抽象类
        public:
        typedef std::shared_ptr<FormatItem> ptr;
        virtual ~FormatItem() {}  //提供虚析构的作用？
        virtual void format(std::ostream& os, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) = 0;
    };
    /*得到相应FormatItem放入m_items*/
    void init();
    bool isError() const { return m_error;}
    const std::string getPattern() const { return m_pattern;}
private:
    std::string m_pattern;                //日志格式模板
    std::vector<FormatItem::ptr> m_items; //日志格式解析后模板
    bool m_error = false;                 //判断日志格式错误
};

/*日志输出目标*/
class LogAppender {
friend class Logger;
public:
    typedef std::shared_ptr<LogAppender> ptr;
    typedef Spinlock MutexType;
    virtual ~LogAppender() {}
    /*输出到流*/
    virtual void log(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) = 0;
    /*输出Yaml格式字符串*/
    virtual std::string toYamlString() = 0;
    /*更改日志格式器*/
    void setFormatter(LogFormatter::ptr val);
    /*获取日志格式器*/
    LogFormatter::ptr getFormatter();
    LogLevel::Level getLevel() const { return m_level;}
    void setLevel(LogLevel::Level val) { m_level = val;}

protected:
    LogLevel::Level m_level = LogLevel::DEBUG; //日志级别
    bool m_hasFormatter = false;               //是否存在formatter
    MutexType m_mutex;                         //互斥锁
    LogFormatter::ptr m_formatter;             //日志格式器
};

/*日志器，定义日志级别，设置输出地，设置日志格式*/
class Logger : public std::enable_shared_from_this<Logger> {
friend class LoggerManager;
public:
    typedef std::shared_ptr<Logger> ptr;
    typedef Spinlock MutexType;
    /*构造函数，root、level、formater均有默认值*/
    Logger(const std::string& name = "root");
    /*将level级别的日志到目标日志中*/
    void log(LogLevel::Level level, LogEvent::ptr event);

    /*添加目标日志*/
    void addAppender(LogAppender::ptr appender);
    /*删除目标日志*/
    void delAppender(LogAppender::ptr appender);
    /*清空目标日志列表*/
    void clearAppenders();

    void debug(LogEvent::ptr event);
    void info(LogEvent::ptr event);
    void warn(LogEvent::ptr event);
    void error(LogEvent::ptr event);
    void fatal(LogEvent::ptr event);
    
    LogLevel::Level getLevel() const { return m_level;}
    void setLevel(LogLevel::Level val) { m_level = val;}

    const std::string& getName() const { return m_name;}

    /*通过智能指针设置formatter，如果appender没有该formatter，则更新*/
    void setFormatter(LogFormatter::ptr val);
    /*通过字符串设置formatter，若格式名没错，则设置*/
    void setFormatter(const std::string& val);
    LogFormatter::ptr getFormatter();

    /*将当前logger的name、level、formatter、appenders的YAML格式按流输出*/
    std::string toYamlString();

private:
    std::string m_name;                       //日志名称
    LogLevel::Level m_level;                  //日志级别
    std::list<LogAppender::ptr> m_appenders;  //日志目标集合
    LogFormatter::ptr m_formatter;            //日志格式器
    Logger::ptr m_root;                       //root log
    MutexType m_mutex;                        //互斥锁
};

class StdoutLogAppender : public LogAppender {
public:
    typedef std::shared_ptr<StdoutLogAppender> ptr;
    void log(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) override;
    std::string toYamlString();
};

class FileLogAppender : public LogAppender {
public:
    typedef std::shared_ptr<FileLogAppender> ptr;
    /*构造函数，初始化文件路径，并且打开文件*/
    FileLogAppender(const std::string& filename);
    /*输出到文件*/
    void log(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) override;
    /*写入文件*/
    bool reopen();
    /*转化为YAML格式字符串*/
    std::string toYamlString();
private:
    std::string m_filename;      //文件路径
    std::ofstream m_filestream;  //文件流
    uint64_t m_lastTime;         //每秒reopen一次，判断文件是否被删除
};

/*日志管理其，单例模式*/
class LoggerManager {
public:
    typedef Spinlock MutexType;
    /*构造函数*/
    LoggerManager();
    /*获取日志器，在map中找到相应的logger，立即返回，否则创建一个logger并放入m_loggers，然后返回*/
    Logger::ptr getLogger(const std::string& name);
    void init();
    Logger::ptr getRoot() const { return m_root;}
    /*将日志格式转化为YAML字符串*/
    std::string toYamlString();

private:
    std::map<std::string, Logger::ptr> m_loggers;  //日志器容器
    Logger::ptr m_root;                            //主日志器
    MutexType m_mutex;                             //互斥锁
};

typedef my_farme::Singleton<LoggerManager> LoggerMgr;

}